<?php
declare(strict_types=1);

namespace App\Controller;

use App\Constants\ErrorCode;
use App\Exception\BusinessException;
use App\Service\AftersaleService;
use App\Service\FileinfoService;
use App\Service\FlyOrderService;
use App\Service\ProductService;
use App\Service\TalkRoomService;
use App\Service\WebSocketService;
use Hyperf\Contract\OnCloseInterface;
use Hyperf\Contract\OnMessageInterface;
use Hyperf\Contract\OnOpenInterface;
use Hyperf\Utils\ApplicationContext;
use Hyperf\Validation\Contract\ValidatorFactoryInterface;
use Hyperf\WebSocketServer\Context;
use Psr\Http\Message\ServerRequestInterface;
use Swoole\Http\Request;
use Swoole\Server;
use Hyperf\Di\Annotation\Inject;
use Swoole\Websocket\Frame;
use Swoole\WebSocket\Server as WebSocketServer;
use Hyperf\WebSocketServer\Sender;
Use Hyperf\Validation\Rule;
use Qbhy\HyperfAuth\Annotation\Auth;
use Qbhy\HyperfAuth\AuthManager;

class WssController extends AbstractController  implements OnMessageInterface, OnOpenInterface, OnCloseInterface
{

    /**
     * @Inject
     * @var AuthManager
     */
    protected  $auth;

    /**
     * @Inject()
     * @var ValidatorFactoryInterface
     */
    protected $validationFactory;

    /**
     * @Inject
     * @var TalkRoomService
     */
    private $talkRoom;

    /**
     * @Inject
     * @var WebSocketService
     */
    private $webSocketService;

    /**
     * 审核
     * @Inject
     * @var ProductService
     */
    private $productService;

    /**
     * 售后单
     * @Inject
     * @var AftersaleService
     */
    private $af;

    /**
     * @Inject
     * @var FileinfoService
     */
    private $fileinfoService;

    /**
     * @Inject
     * @var FlyOrderService
     */
    private $orderService;

    /**
     * @Inject
     * @var ServerRequestInterface
     */
    protected $srequest;

    /**
     * @Inject
     * @var Sender
     */
    protected $sender;


//    test黑名单
    public function black_list()
    {
        $max_count = 5;//        5分钟内的频率
        $count_time = 60*5;//    五分钟内时间
        $black_time  = 60*10;//        黑名单时间
        $ip = $this->getIp();//        获取ip
//        实例化redis
        $container = ApplicationContext::getContainer();
        $redis = $container->get(\Hyperf\Redis\Redis::class);

        //                检测黑名单
        $black_key = 'black_'.$ip;
        if($redis->get($black_key)){
            return $this->jsonResponse->error(401,'已在黑名单中，请稍后重试');
        }else{
            $count_key = 'count_'.$ip;
            $microtime  = microtime();
            $time  = time();
            $redis->zAdd($count_key,$time,$microtime);//                正常统计返回
            $time_start  = $time-$count_time;
            $time_end  = $time;
//            从当前时间往前推五分钟，统计次数
            $count =$redis->zCount($count_key,strval($time_start),strval($time_end));
            if($count>$max_count){
//                加入黑名单
                $redis->set($black_key,$black_time,$black_time);
                return $this->jsonResponse->error(401,'已加入在黑名单中，请10分钟后重试');
            }else{
                return $this->jsonResponse->success([],'成功访问');
            }
        }
    }

    public function on_msg()
    {
        $request_data = $this->request->all();
        $userinfo =  $this->auth->user();
        $func  = $request_data['func'];
        $data  = $request_data['data'];
        $validator  = $this->validationFactory->make(
            $data,
            [
                'product_id'=>[
                    Rule::requiredIf($data['log_type']==5)
                ],
                'af_sn'=>[
                    Rule::requiredIf($data['log_type']==6)
                ],
                'af_user_id'=>[
                    Rule::requiredIf($data['log_type']==6)
                ],
                'link_id'=>'required|min:1|integer',
                'fileinfo_id'=>[
                    Rule::requiredIf(($data['log_type']==2||$data['log_type']==3))
                ],
                'log_type'=>'|min:1|max:2',
                'type'=>'min:1|max:2',
                'room_id'=>'required|min:1|integer',
            ],
            );

        if ($validator->fails()) {
            return $this->jsonResponse->success($data,implode(',', $validator->errors()->all()));
        }else{
            $data['send_by'] =   $user_id = $data['link_id'];
            go(function () use($data){
                $this->talkRoom->create_log($data);
            });
            $data['code'] = 200;
            if($data['log_type']==2||$data['log_type']==3){
                //             查询文件详情
                $data['url']  =    $this->fileinfoService->getFileinfo([$data['fileinfo_id']],1);
            }elseif ($data['log_type']==4){
                //             查询订单详情
                $data['order_info']  =  $this->orderService->getDetailsByOrderId(intval($data['order_id']));
            }elseif ($data['log_type']==5){
                //             查询商品详情
                $data['product_info']  =  $this->productService->info(intval($data['product_id']));
            }elseif ($data['log_type']==6){
                //售后单列表
                $details =  $this->af->afDetails($data['af_sn'],intval($data['af_user_id']));
                $details['virtual_form_info_arr'] = empty($details['with_order']['virtual_form_info']) ? [] : json_decode($details['with_order']['virtual_form_info'], true);
                if (!empty($details['virtual_form_info_arr'])) {
                    if ($details['af_order_style'] == 2) {
                        $details['sku_name'] = date("m月d日,H:i", $details['virtual_form_info_arr']['date_time']) . ',' . $details['virtual_form_info_arr']['people_num'] . "人," . ($details['virtual_form_info_arr']['is_vip'] ? "包间" : "大厅");
                        $details['sku_img'] = $details['virtual_form_info_arr']['logo'] ?? "";
                    }
                }
                $data['after_sale_info']  = $details;
            }else{

            }
        }
        return $this->jsonResponse->success($data,'返回数据');
    }
    public function onMessage($server, Frame $frame): void
    {
        $request_data = json_decode($frame->data,true);
        $func  = $request_data['func']??'ping';
        $data  = $request_data['data']??[];
        if(isset($data['room_id'])){
            Context::set('room_id', $data['room_id']);
        }
        switch ($func){
            case 'say':
                //        每次发消息，更新fd的ttl，insertOrupdate用户的fd
                $this->webSocketService->ws_redis_retrieve(intval($frame->fd),intval(Context::get('link_id')));
                $data['send_by'] =  Context::get('link_id');
                $data['send_time'] = time();
                $validator  = $this->validationFactory->make(
                    $data,
                    [
                        'product_id'=>[
                            Rule::requiredIf($data['log_type']==5)
                        ],
                        'af_sn'=>[
                            Rule::requiredIf($data['log_type']==6)
                        ],
                        'af_user_id'=>[
                            Rule::requiredIf($data['log_type']==6)
                        ],
                        'link_id'=>'required|min:1|integer',
                        'fileinfo_id'=>[
                            Rule::requiredIf(($data['log_type']==2||$data['log_type']==3))
                        ],
                        'log_type'=>'required|min:1|max:6',
                        'type'=>'required|min:1|max:2',
                        'room_id'=>'required|min:1|integer',
                    ],
                    );
                if ($validator->fails()) {
                    $res_data = $this->jsonResponse->ws_success($data,'返回数据');
                    $res_data['code'] = 500;
                    $res_data['msg'] = implode(',', $validator->errors()->all());
                }else{
                    go(function () use($data){
                        $this->talkRoom->create_log($data);
                    });
                    $data['code'] = 200;
                    if($data['log_type']==2||$data['log_type']==3){
                        //             查询文件详情
                        $data['url']  =    $this->fileinfoService->getFileinfo([$data['fileinfo_id']],1);
                    }elseif ($data['log_type']==4){
                        //             查询订单详情
                        $data['order_info']  =  $this->orderService->getDetailsByOrderId(intval($data['order_id']));
                    }elseif ($data['log_type']==5){
                        //             查询商品详情
                        $data['product_info']  =  $this->productService->info(intval($data['product_id']));
                    }elseif ($data['log_type']==6){
                        //售后单列表
                        $details =  $this->af->afDetails($data['af_sn'],intval($data['af_user_id']));
                        $details['virtual_form_info_arr'] = empty($details['with_order']['virtual_form_info']) ? [] : json_decode($details['with_order']['virtual_form_info'], true);
                        if (!empty($details['virtual_form_info_arr'])) {
                            if ($details['af_order_style'] == 2) {
                                $details['sku_name'] = date("m月d日,H:i", $details['virtual_form_info_arr']['date_time']) . ',' . $details['virtual_form_info_arr']['people_num'] . "人," . ($details['virtual_form_info_arr']['is_vip'] ? "包间" : "大厅");
                                $details['sku_img'] = $details['virtual_form_info_arr']['logo'] ?? "";
                            }
                        }
                        $data['after_sale_info']  = $details;
                    }else{

                    }
                    $res_data = $this->jsonResponse->ws_success($data,'返回数据');
                }
                $server->push($frame->fd,json_encode($res_data));
                $this->send_to_room($res_data,intval($data['link_id']));
                break;
            case 'ack':
                $this->webSocketService->ws_redis_retrieve(intval($frame->fd),intval($data['link_id']));
                $validator  = $this->validationFactory->make(
                    $data,
                    [
                        'type'=>'required|integer|min:1|max:2',
                        'room_id'=>[
                            'required',
                            'integer',
                            'min:1',
                            Rule::exists('fly_talk_room','id')
                        ],
                        'link_id'=>[
                            'required',
                            'integer',
                            'min:1',
//                            Rule::exists('fly_talk_room_user','link_id')->where('type',$data['type'])
                        ],
                    ],
                );

                if ($validator->fails()) {
                    $res_data = $this->jsonResponse->ws_success($data,'返回数据');
                    $res_data['code'] = 500;
                    $res_data['msg'] = implode(',', $validator->errors()->all());
                }else{
                    $room_id = $data['room_id']??1;
                    if($this->talkRoom->check_user(intval($room_id),intval($data['link_id']),intval($data['type']))){
                        $this->talkRoom->update_ack(intval($room_id),intval($data['link_id']),intval($data['type']));
                    };
                    $data['room_user_last_ack'] = $this->talkRoom->room_user_last_ack(intval($room_id));
                    $res_data = $this->jsonResponse->ws_success($data,'返回数据');
                }
                $server->push($frame->fd,json_encode($res_data));
                break;
            case 'ping':
                $this->webSocketService->ws_redis_retrieve(intval($frame->fd),intval($data['link_id']));
                $res_data['code'] = 200;
                $res_data['msg'] = '心跳检测ok';
                $server->push($frame->fd,json_encode($res_data));
                break;
            default:
                break;
        }
    }

    public function onClose($server, int $fd, int $reactorId): void
    {

        //        关闭时，删除fd和，用户链接
        $link_id  = Context::get('link_id');
        $this->webSocketService->ws_redis_del(intval($fd),intval($link_id));
    }

    public function onOpen($server, Request $request): void
    {
        $link_id  = Context::get('link_id');
//        建立连接时，存下fd
        $this->webSocketService->ws_redis_add(intval($request->fd),intval($link_id));
        $server->push($request->fd, 'Opened');
    }

    public function send_to_room(array $data,int $remove_user_id=0)
    {
        $user_list = $this->talkRoom->room_user(intval($data['data']['room_id']));
        $link_ids = array_column($user_list,'link_id','link_id');
        var_dump($remove_user_id);
        var_dump($link_ids);
        if($remove_user_id){
            //            是否排除本人
            if(isset($link_ids[$remove_user_id])){
                unset($link_ids[$remove_user_id]);
            }
        }
        var_dump($link_ids);
        foreach ($link_ids as $key=>$value){
            $user_fd_key = 'fd_user_'.$value;
            $fd = $this->talkRoom->redis_get($user_fd_key);
            if($fd){
                $this->sender->push($fd, json_encode($data));
            }
        }
    }
}
